package com.sinothk.gpserver.modules.comm.serialNumber.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sinothk.gpserver.modules.comm.serialNumber.entity.SerialNumber;
import com.sinothk.gpserver.modules.comm.serialNumber.mapper.SerialNumberMapper;
import com.sinothk.gpserver.modules.comm.serialNumber.service.SerialNumberService;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author liangyt
 * @since 2021-01-29
 */
@Service
public class SerialNumberServiceImpl extends ServiceImpl<SerialNumberMapper, SerialNumber> implements SerialNumberService {

    /**
     * 格式
     */
    private String pattern = "";

    // 时间格式
//    private String formatType = "yyyyMMddHH";
//    private String formatType = "yyyyMMdd";
    private String formatType = "yyMMdd";

    /**
     * 生成器锁
     */
    private final ReentrantLock lock = new ReentrantLock();

    /**
     * 流水号格式化器
     */
    private DecimalFormat format = null;

    /**
     * 预生成锁
     */
    private final ReentrantLock prepareLock = new ReentrantLock();

    private final ReentrantLock serialLock = new ReentrantLock();

    /**
     * 最小值
     */
    private int min = 0;

    /**
     * 最大值
     */
    private long max = 0;

    /**
     * 已生成流水号（种子）
     */
    private long seed = min;

    /**
     * 预生成数量
     */
    private int prepare = 0;

    /**
     * 数据库存储的当前最大序列号
     **/
    long maxSerialInt = 0;

    /**
     * 当前序列号是否为个位数自增的模式
     **/
    private String isAutoIncrement = "0";

    private String curDateStr;

    /**
     * 预生成流水号
     */
    HashMap<String, List<String>> prepareSerialNumberMap = new HashMap<>();

    /**
     * 根据模块code生成预数量的序列号存放到Map中
     *
     * @param moduleCode 模块code
     * @return
     */
    @CachePut(value = "serialNumber", key = "#moduleCode")
    public List<String> generatePrepareSerialNumbers(String moduleCode, SerialNumber systemSerialNumber) {
        //临时List变量
        List<String> resultList = new ArrayList<String>(prepare);
        String currentDate = DateFormatUtils.format(new Date(), formatType);
        lock.lock();
        try {
            for (int i = 0; i < prepare; i++) {
                maxSerialInt = maxSerialInt + 1;
                if (maxSerialInt > min && (maxSerialInt + "").length() < max) {
                    seed = maxSerialInt;
                } else {
                    //如果动态数字长度大于模板中的长度 例：模板CF000  maxSerialInt 1000
                    seed = maxSerialInt = 1;
                    //更新数据，重置maxSerialInt为1
                    systemSerialNumber.setMaxSerial("1");

                    this.saveOrUpdate(systemSerialNumber);
                }
                if (!curDateStr.equals(currentDate) && pattern.contains("${DATE}")) {
                    seed = maxSerialInt = 1;
                }
                //动态数字生成
                String formatSerialNum = format.format(seed);

                //动态日期的生成
                if (pattern.contains("${DATE}")) {
                    formatSerialNum = formatSerialNum.replace("${DATE}", currentDate);
                }
                resultList.add(formatSerialNum);
            }
            //更新数据
            systemSerialNumber.setMaxSerial(maxSerialInt + "");
            systemSerialNumber.setCurDateStr(currentDate);

            this.saveOrUpdate(systemSerialNumber);
        } finally {
            lock.unlock();
        }
        return resultList;
    }

    /**
     * 根据模块code生成序列号
     *
     * @param moduleCode 模块code
     * @return 序列号
     */
    public String generateSerialNumberByModelCode(String moduleCode) {

        //预序列号加锁
        prepareLock.lock();
        try {
            //判断内存中是否还有序列号
            if (null != prepareSerialNumberMap.get(moduleCode) && prepareSerialNumberMap.get(moduleCode).size() > 0) {
                //若有，返回第一个，并删除
                return prepareSerialNumberMap.get(moduleCode).remove(0);
            }
        } finally {
            //预序列号解锁
            prepareLock.unlock();
        }

        serialLock.lock();
        SerialNumber systemSerialNumber;
        try {
            LambdaQueryWrapper<SerialNumber> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SerialNumber::getModuleCode, moduleCode).orderByDesc(SerialNumber::getMaxSerial);

            List<SerialNumber> list = this.list(queryWrapper);

            systemSerialNumber = list.remove(0);
            for (SerialNumber serialNumberEntity : list) {
                this.removeById(serialNumberEntity.getId());
            }
            prepare = Integer.parseInt(systemSerialNumber.getPreMaxNum().trim());//预生成流水号数量
            pattern = systemSerialNumber.getConfigTemplet().trim();//配置模板
            String maxSerial = systemSerialNumber.getMaxSerial().trim(); //存储当前最大值
            isAutoIncrement = systemSerialNumber.getIsAutoIncrement().trim();
            maxSerialInt = Long.parseLong(maxSerial.trim());//数据库存储的最大序列号
            max = this.counter(pattern, '0') + 1;//根据模板判断当前序列号数字的最大值
            if (isAutoIncrement.equals("1")) {
                pattern = pattern.replace("0", "#");
            }
            format = new DecimalFormat(pattern);
            curDateStr = systemSerialNumber.getCurDateStr();
            if (StringUtils.isBlank(curDateStr)) {
                curDateStr = DateFormatUtils.format(new Date(), formatType);
            }
        } finally {
            serialLock.unlock();
        }
        //生成预序列号，存到缓存中
        List<String> resultList = generatePrepareSerialNumbers(moduleCode, systemSerialNumber);
        prepareLock.lock();
        try {
            prepareSerialNumberMap.put(moduleCode, resultList);
            return prepareSerialNumberMap.get(moduleCode).remove(0);
        } finally {
            prepareLock.unlock();
        }
    }

    /**
     * 设置最小值
     *
     * @param value 最小值，要求：大于等于零
     * @return 流水号生成器实例
     */
    public SerialNumberService setMin(int value) {
        lock.lock();
        try {
            this.min = value;
        } finally {
            lock.unlock();
        }
        return this;
    }

    /**
     * 最大值
     *
     * @param value 最大值，要求：小于等于Long.MAX_VALUE ( 9223372036854775807 )
     * @return 流水号生成器实例
     */
    public SerialNumberService setMax(long value) {
        lock.lock();
        try {
            this.max = value;
        } finally {
            lock.unlock();
        }
        return this;
    }

    /**
     * 设置预生成流水号数量
     *
     * @param count 预生成数量
     * @return 流水号生成器实例
     */
    public SerialNumberService setPrepare(int count) {
        lock.lock();
        try {
            this.prepare = count;
        } finally {
            lock.unlock();
        }
        return this;
    }

    /**
     * 统计某一个字符出现的次数
     *
     * @param str 查找的字符
     * @param c
     * @return
     */
    private int counter(String str, char c) {
        int count = 0;
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == c) {
                count++;
            }
        }
        return count;
    }
}
